Catalyst / admin/Strike 14.8 GB / 57.8 GB 40.0 GB free
Help Sign in

admin / Strike

public

Web-Based UK Cyber Compliance Tool with Reporting

Code Issues Pull requests Pipelines Packages Security Insights Wiki Settings
Strike / StrikeXi v3 / backend / app / schemas.py 4924 B · main
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
import uuid
from datetime import date, datetime
from typing import Optional

from pydantic import BaseModel


class Token(BaseModel):
    access_token: str
    token_type: str = "bearer"
    username: str
    role: str
    must_change_password: bool = False
    mfa_required: bool = False
    mfa_enabled: bool = False
    first_login_pending: bool = False


class MfaChallenge(BaseModel):
    """Returned when a password is valid but a TOTP code is still required."""
    mfa_required: bool = True
    mfa_token: str          # short-lived token proving the password step passed
    username: str


# ---------- Self-service signup ----------
class SignupCreate(BaseModel):
    surname: str
    first_name: str
    company_name: str
    email: str
    contact_number: str
    username: str
    password: str


# ---------- User administration ----------
class UserOut(BaseModel):
    id: uuid.UUID
    username: str
    full_name: Optional[str] = None
    role: str
    is_active: bool
    must_change_password: bool
    mfa_enabled: bool
    mfa_required: bool
    # V2 profile / origin fields
    first_name: Optional[str] = None
    surname: Optional[str] = None
    company_name: Optional[str] = None
    email: Optional[str] = None
    contact_number: Optional[str] = None
    signup_origin: str = "admin"
    first_login_pending: bool = False
    last_login: Optional[datetime] = None
    created_at: datetime

    class Config:
        from_attributes = True


class UserCreate(BaseModel):
    username: str
    password: str
    full_name: Optional[str] = None
    role: str = "assessor"
    mfa_required: bool = False
    must_change_password: bool = True
    # Optional profile fields when an admin creates an account
    first_name: Optional[str] = None
    surname: Optional[str] = None
    company_name: Optional[str] = None
    email: Optional[str] = None
    contact_number: Optional[str] = None


class UserUpdate(BaseModel):
    full_name: Optional[str] = None
    role: Optional[str] = None
    is_active: Optional[bool] = None
    password: Optional[str] = None
    mfa_required: Optional[bool] = None
    must_change_password: Optional[bool] = None


class PasswordChange(BaseModel):
    current_password: str
    new_password: str


class MfaSetupOut(BaseModel):
    secret: str
    uri: str
    qr: str


class MfaConfirm(BaseModel):
    code: str


class AnswerOptionOut(BaseModel):
    id: uuid.UUID
    label: str
    score: float

    class Config:
        from_attributes = True


class QuestionOut(BaseModel):
    id: uuid.UUID
    code: str
    text: str
    weight: float
    principle_id: str
    options: list[AnswerOptionOut]

    class Config:
        from_attributes = True


class PrincipleOut(BaseModel):
    id: str
    objective_id: str
    title: str
    description: Optional[str]
    questions: list[QuestionOut] = []


class ObjectiveOut(BaseModel):
    id: str
    title: str
    description: Optional[str]
    principles: list[PrincipleOut] = []


# ---------- Frameworks (v3) ----------
class FrameworkOut(BaseModel):
    id: str
    name: str
    description: Optional[str] = None
    version: Optional[str] = None
    is_active: bool = True

    class Config:
        from_attributes = True


class AssessmentCreate(BaseModel):
    organisation_name: str
    assessment_date: Optional[date] = None
    framework_id: str = "caf"


class AnswerIn(BaseModel):
    question_id: uuid.UUID
    option_id: uuid.UUID


class AnswersSubmit(BaseModel):
    answers: list[AnswerIn]


class ReportMeta(BaseModel):
    organisation_name: Optional[str] = None
    assessment_date: Optional[date] = None


# ---------- Evidence / notes (v3) ----------
class NoteIn(BaseModel):
    """Text note (assessment-level when question_id is omitted)."""
    content: str
    question_id: Optional[uuid.UUID] = None


class EvidenceOut(BaseModel):
    id: uuid.UUID
    question_id: Optional[uuid.UUID] = None
    kind: str
    content: Optional[str] = None
    file_name: Optional[str] = None
    file_size: Optional[int] = None
    created_by: str
    created_at: datetime

    class Config:
        from_attributes = True


# ---------- Score revisions (v3) ----------
class RevisionOut(BaseModel):
    id: uuid.UUID
    revision_no: int
    label: Optional[str] = None
    overall_score: Optional[float] = None
    snapshot: dict
    triggered_by: str
    created_at: datetime

    class Config:
        from_attributes = True


class AssessmentOut(BaseModel):
    id: uuid.UUID
    framework_id: str = "caf"
    organisation_name: str
    assessment_date: date
    status: str
    overall_score: Optional[float]
    created_at: datetime

    class Config:
        from_attributes = True


class AuditOut(BaseModel):
    id: int
    ts: datetime
    username: Optional[str]
    action: str
    detail: Optional[str]
    ip_address: Optional[str]

    class Config:
        from_attributes = True